package top.zhangjianyong.tools.indicator.strategy.impl;

import org.springframework.stereotype.Component;
import top.zhangjianyong.tools.entity.ETFData;
import top.zhangjianyong.tools.indicator.context.IndicatorContext;
import top.zhangjianyong.tools.indicator.model.TechnicalIndicatorResult;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;

/**
 * EMA（指数移动平均线）指标计算器
 * 支持5、10、20日EMA
 *
 * @author zhangjianyong
 * @date 2024/01/01
 */
@Component
public class EMAIndicator extends AbstractIndicator {

    private static final String INDICATOR_NAME = "EMA";

    @Override
    public String getName() {
        return INDICATOR_NAME;
    }

    @Override
    public int getMinDataSize() {
        return 20; // 最大周期是20日
    }

    @Override
    protected TechnicalIndicatorResult doCalculate(IndicatorContext context) {
        List<ETFData> dataList = context.getDataList();
        TechnicalIndicatorResult result = TechnicalIndicatorResult.success(getName());

        // 计算5日EMA
        BigDecimal ema5 = calculateEMA(dataList, 5);
        if (ema5 != null) {
            result.addValue("ema_5", ema5);
        }

        // 计算10日EMA
        BigDecimal ema10 = calculateEMA(dataList, 10);
        if (ema10 != null) {
            result.addValue("ema_10", ema10);
        }

        // 计算20日EMA
        BigDecimal ema20 = calculateEMA(dataList, 20);
        if (ema20 != null) {
            result.addValue("ema_20", ema20);
        }

        return result;
    }

    /**
     * 计算指定周期的EMA
     * EMA公式：EMA(today) = (Price(today) * K) + (EMA(yesterday) * (1 - K))
     * 其中 K = 2 / (period + 1)
     *
     * @param dataList K线数据列表（按时间顺序，早到晚）
     * @param period   周期
     * @return EMA值
     */
    private BigDecimal calculateEMA(List<ETFData> dataList, int period) {
        if (dataList.size() < period) {
            return null;
        }

        // 平滑系数
        BigDecimal k = new BigDecimal(2)
            .divide(new BigDecimal(period + 1), 8, RoundingMode.HALF_UP);
        BigDecimal oneMinusK = BigDecimal.ONE.subtract(k);

        // 初始值使用第一个价格
        BigDecimal ema = dataList.get(0).getValue();

        // 从第二个数据点开始计算EMA
        for (int i = 1; i < dataList.size(); i++) {
            BigDecimal price = dataList.get(i).getValue();
            ema = price.multiply(k).add(ema.multiply(oneMinusK));
        }

        return ema.setScale(4, RoundingMode.HALF_UP);
    }
}

